package pacBioSimple;

import java.util.ArrayList;
import java.util.List;


import tools.kmer.KmerSet_binary;


public class PacBioMaster 
{
	private KmerSet_binary ks=null;
	private List<Integer> seeds= null;
	private CodingTable table = null;
	private int kmer_count_limit=170;
	private KKmerList kmerVector = null;
	private Entropy<Character> entropy = null;
	private int linker_counter=0;
	private boolean linker=false;
	private double entropy_cutoff=0.0;
	private boolean contamination =false;
//	private boolean perfect =false;
//	private PacBioReadContainer read = null;
//	private Logger log = Logger.getLogger(PacBioKmerAlgoMain.class.getName());

	public PacBioMaster(KmerSet_binary ks,CodingTable table, int kmer_count_limit,KKmerList kmerVector,double entropy_cutoff)
	{
		this.table=table;
		this.ks=ks;
		this.kmer_count_limit=kmer_count_limit;
		this.kmerVector=kmerVector;
		List<Character> alphabet = new ArrayList<Character>();
		alphabet.add('A');
		alphabet.add('C');
		alphabet.add('G');
		alphabet.add('T');
		entropy = new Entropy<Character>(alphabet);
		this.entropy_cutoff=entropy_cutoff;
	}
	
	public int seperateSeeding(PacBioReadContainer read)
	{
		StringBuilder readSequence = read.getOrginalSequence();
		seeding(readSequence); // first step seeding
		//log.log(Level.WARNING, seeds.toString());
		return seeds.size();
	}
	
	public List<Double> seedEntropy(PacBioReadContainer read,Entropy<Character> entropy)
	{
		StringBuilder readSequence = read.getOrginalSequence();
		seeding2(readSequence); // first step seeding
		List<Double> entropyValues = new ArrayList<Double>();
		for(int i=0; i < this.seeds.size();++i)
		{
			entropyValues.add(entropy.calcEntropy(readSequence.substring(seeds.get(i), seeds.get(i)+ks.getKmerSize())));
		}
		return entropyValues;
	}
	
	public Pair<List<Double>,List<Double>> sequenceEntropy(PacBioReadContainer read,Entropy<StringBuilder> entropy, Entropy<Character> alphabete)
	{
		StringBuilder readSequence = read.getOrginalSequence();
		seeding2(readSequence); // first step seeding
		List<Double>entropyValues = new ArrayList<Double>();
		System.out.println(this.seeds.toString());
		for(int i=0; i < this.seeds.size();++i)
		{
			System.out.println(i+" "+this.seeds.get(i));
			if(i==0 && seeds.get(i)!=0)
			{
				entropyValues.add(entropy.calcEntropyDimer(readSequence.substring(0,seeds.get(i))));
			}
			if(i+1<this.seeds.size())
			{
				if(this.seeds.get(i+1)>=readSequence.length())
				{
					entropyValues.add(entropy.calcEntropyDimer(readSequence.substring(seeds.get(i),readSequence.length())));
				}
				else
				{
//					if(log.getLevel()==Level.ALL)
//					{
//						System.out.println("Values "+readSequence.length()+" "+seeds.get(i)+" "+(seeds.get(i)+ks.getKmerSize())+" "+this.seeds.get(i+1));
//						log.log(Level.ALL,"Values "+readSequence.length()+" "+seeds.get(i)+" "+(seeds.get(i)+ks.getKmerSize())+" "+this.seeds.get(i+1));
//					}
					entropyValues.add(entropy.calcEntropyDimer(readSequence.substring(seeds.get(i)+ks.getKmerSize(),this.seeds.get(i+1))));
				}
			}
			else
			{
				//System.out.println("Values "+readSequence.length()+" "+seeds.get(i)+" "+seeds.get(i)+ks.getKmerSize());
				if(seeds.get(i)+ks.getKmerSize()>= readSequence.length())
				{
					entropyValues.add(entropy.calcEntropyDimer(readSequence.substring(seeds.get(i),readSequence.length())));
					//System.out.println("ERROR "+readSequence.length()+" "+seeds.get(i)+" "+seeds.get(i)+ks.getKmerSize());
				}
				else
				{
					entropyValues.add(entropy.calcEntropyDimer(readSequence.substring(seeds.get(i)+ks.getKmerSize(),readSequence.length())));
				}
			}
		}		
		List<Double> entropyValues2 = new ArrayList<Double>();
		for(int i=0; i < this.seeds.size();++i)
		{
			entropyValues2.add(alphabete.calcEntropy(readSequence.substring(seeds.get(i), seeds.get(i)+ks.getKmerSize())));
		}
		return new Pair<List<Double>, List<Double>>(entropyValues2, entropyValues); 
	}
	
	public List<Integer> getSeeds()
	{
		return this.seeds;
	}
	
	
	public void run_singleAlignmentThreaded(PacBioReadContainer read)
	{
		StringBuilder readSequence = read.getOrginalSequence();
		linker=false;
		//perfect=false;
		//System.out.println(read.getHeader());
		//System.out.println(read.getOrginalSequence());
//		log.log(Level.WARNING,read.getHeader()+"\n"+read.getOrginalSequence()+"\n");
		//System.out.println(read.getHeader()+"\n"+read.getOrginalSequence()+"\n");
		seeding2(readSequence); // first step seeding
		
		MyUtil.logging(seeds.toString());
		//System.out.println(seeds.toString());
		
		//System.exit(1);
		//log.log(Level.WARNING, seeds.toString());
		//System.out.println(seeds.toString());
		//System.out.println(seeds.toString());
		if(seeds.size()==0)
		{
			read.setCorrectedSequence(read.getOrginalSequence());
			StringBuilder st = new StringBuilder();
			for(int i=0; i < read.getOrginalSequence().length();++i)
			{
				st.append("S");
			}
			read.setCodingSeqeunce(st);
			read.setMarkedSequence(read.getOrginalSequence());
			//perfect=false;
		}
		else if(seeds.size()!=0)
		{
			try
			{
				PacBioBridgeMainObject helperObject = new PacBioBridgeMainObject(seeds, table, readSequence, ks,this.kmer_count_limit,kmerVector);
				List<Path> candidatesList = new ArrayList<Path>();
				//List<List<StringBuilder>> candidatesList = new ArrayList<List<StringBuilder>>();
				List<Boolean>signal = new ArrayList<Boolean>();
				
				for(int i=0; i <= seeds.size()-1;++i) 
				{
					MyUtil.logging(i +" "+ seeds.get(i)+" seed extraction");
					//System.out.println();
					if(i==0 && seeds.get(i)==0)
					{
						if(seeds.size()>1)
						{
							helperObject.inbetweenSeed(candidatesList, 0,signal);
						}
						else
						{
							helperObject.lastSeeds(candidatesList,signal);
						}
							
						
					}
					if(i==0 && seeds.get(i)!=0)
					{ 
						helperObject.firstSeedReverse(candidatesList, signal);
						
						if(seeds.size()>1)
						helperObject.inbetweenSeed(candidatesList, 0,signal);

					}
					if(i== seeds.size()-1 && seeds.get(i)+ks.getKmerSize()!=readSequence.length()-1)
					{
						helperObject.lastSeeds(candidatesList,signal);
						
					}
					else if(i>0 && i<seeds.size()-1)
					{
						helperObject.inbetweenSeed(candidatesList, i,signal);
					}
				}
				//contamination
				if(helperObject.isContamination())
				{
					read.setContaminationBreakPoints(helperObject.getContaminationBreakPoints());
					this.contamination=true;
				}
				else
				{		
					if(helperObject.getLinker())
					{
						this.linker=true;
					}
					//now do the alignment;
					List<Path> result = new ArrayList<Path>();
					MyUtil.logging("alignment step");
					//	System.out.println("alignment step");
					
					helperObject.callAlignment2(candidatesList, result);
					candidatesList=null;
					//	System.out.println("results: "+result.toString());
					//	System.out.println("fragment prep");
					MyUtil.logging("results: "+result.toString());
					MyUtil.logging("fragment prep");
					
					List<String>endResult= new ArrayList<String>();
					List<String>marked = new ArrayList<String>();
					List<String>corrected = new ArrayList<String>();

					helperObject.prepareFragments2(result, endResult, marked, corrected);
				
					//System.out.println("done Alignment");
					
					//now we have to trim the reads
					//trimBeginAndEnd(result, readSequence);
					
					//now glue each fragment together!
					MyUtil.logging("endResult "+ endResult.toString());
					MyUtil.logging("combine fragements");
//					System.out.println("endResult "+ endResult.toString());
//					System.out.println("marked "+ marked.toString());
//					System.out.println("corrected "+ corrected.toString());
					//System.out.println("combine fragements");
					
					helperObject.glueFragments(result,endResult,marked,corrected);
					
					linker_counter=helperObject.getCounter();
					
					read.setCorrectedSequence(new StringBuilder(endResult.get(0)));
					read.setCodingSeqeunce(new StringBuilder(marked.get(0)));
					read.setMarkedSequence(new StringBuilder(corrected.get(0)));
//					System.out.println("RESULT");
//					System.out.println(read.toString());
					endResult=null;
					marked=null;
					corrected=null;
				}
			}
			catch(Exception e)
			{
				System.out.println(read.toString());
				e.printStackTrace();
				System.exit(1);
			}
		}
//		else
//		{
//			if(seeds.size()==0)
//			{
//				//System.out.println("SeedSize: "+ 0);
//				//System.out.println(seeds.toString());
//			}
//			else
//			{
//				//System.out.println("SeedSize: "+ seeds.size());
//				//System.out.println(seeds.toString());
//			}
//		}
	}
	
	
	
	public int getLinker_counter() {
		return linker_counter;
	}

	public void setLinker_counter(int linker_counter) {
		this.linker_counter = linker_counter;
	}

//	private void seeding2(StringBuilder readSequence)
//	{
//		this.seeds = new ArrayList<Integer>();
//		boolean found=false;
//		int counter=0;
//		int position=-1;
//		for(int i=0; i <readSequence.length()-ks.getKmerSize()+1;++i)
//		{
//			//build kmer
//			String kmer = readSequence.substring(i, i+ks.getKmerSize());
//			int kmer_count=ks.checkKmerDistribution(kmer);
//			if(kmer_count!=0 && kmer_count<this.kmer_count_limit)
//			{
//				if(!found)
//				{
//					System.out.println("found first one "+ i);
//					position=i;
//					found=true;
//				}
//				System.out.println("increase counter ");
//				++counter;
//			}
//			else
//			{
//				System.out.println("not valid kmer "+ counter+ " "+position);
//				if(counter!=0)
//				{
//					System.out.println("not rep kmer");
//					int halfResult=counter/2;
//					seeds.add(position+halfResult);
//					position=-1;
//					counter=0;
//					found=false;
//				}
//			}
//		}
//	System.out.println(seeds.toString());
//		if(counter!=0)
//		{
//			System.out.println("counter is not 0 "+counter+ " "+position);
//			int halfResult=counter/2;
//			System.out.println(halfResult);
//			//if(position+halfResult-ks.getKmerSize()<=seeds.get(seeds.size()-1)+ks.getKmerSize())
//			{
//				//int dis = position+halfResult-seeds.get(seeds.size()-1)/2;
//				seeds.add(position+halfResult);
//			}
//		}
//	}
//	
	
	public boolean getLinker()
	{
		return linker;
	}
	private void seeding2(StringBuilder readSequence)
	{
		this.seeds = new ArrayList<Integer>();
		boolean found=false;
		int counter=0;
		int position=-1;
	//	System.out.println(ks.getKmerSize());
	//	System.out.println(readSequence.length());
		for(int i=0; i <readSequence.length()-ks.getKmerSize()+1;++i)
		{
			//build kmer
			String kmer = readSequence.substring(i, i+ks.getKmerSize());
			int kmer_count=ks.checkKmerDistribution(kmer);
			if(kmer_count!=0 && kmer_count<this.kmer_count_limit)
			{
				if(!found)
				{
					//System.out.println("found first one "+ i);
					position=i;
					found=true;
				}
				//System.out.println("increase counter ");
				++counter;
			}
			else
			{
				//System.out.println("not valid kmer "+ counter+ " "+position);
				if(counter!=0 && kmer_count==0)
				{
					//System.out.println("not rep kmer");
					int halfResult=counter/2;
					
					
					int begin_pos = 0;
					if(position+halfResult-(ks.getKmerSize()/2) >0)
					{
						begin_pos= position+halfResult-(ks.getKmerSize()/2);
					}
					int end_pos = readSequence.length();
					
					if(position + halfResult+(ks.getKmerSize()/2)<=readSequence.length())
					{
						end_pos=position + halfResult+(ks.getKmerSize()/2);
					}					
					double entropy_newSeed=entropy.calcEntropy(readSequence.substring(begin_pos, end_pos)); 
					if(entropy_newSeed>=entropy_cutoff)
					{						
						if(seeds.size()>0 && seeds.get(seeds.size()-1)+ks.getKmerSize()>=position+halfResult-(ks.getKmerSize()/2))
						{
							//select those seeds, with the highest entropy
							if(entropy.calcEntropy(readSequence.substring(seeds.get(seeds.size()-1)))<=entropy_newSeed)
							{
								seeds.set(seeds.size()-1,position+halfResult-(ks.getKmerSize()/2));
							}
						}
						else
						{
							seeds.add(position+halfResult-(ks.getKmerSize()/2));
						}
					}
					position=-1;
					counter=0;
					found=false;
				}
				else if(found) //rep kmer go over it
				{
					//System.out.println("increasing counter rep kmer");
					++counter;
				}
			}
		}
		//System.out.println(seeds.toString());
		if(counter!=0)
		{
			if(counter<readSequence.length()-ks.getKmerSize()) //perfect match
			{
				//System.out.println("counter is not 0 "+counter+ " "+position);
				int halfResult=counter/2;
				//System.out.println(halfResult);
				double entropy_newSeed=entropy.calcEntropy(readSequence.substring(position+halfResult-(ks.getKmerSize()/2), position + halfResult+(ks.getKmerSize()/2))); 
				if(entropy_newSeed>=entropy_cutoff)
				{
					if(seeds.size()>0&&seeds.get(seeds.size()-1)+ks.getKmerSize()>=position+halfResult-(ks.getKmerSize()/2))
					{
						//select those seeds, with the highest entropy
						if(entropy.calcEntropy(readSequence.substring(seeds.get(seeds.size()-1)))<=entropy_newSeed)
						{
							seeds.set(seeds.size()-1,position+halfResult-(ks.getKmerSize()/2));
						}
					}
					else
					{
						seeds.add(position+halfResult-(ks.getKmerSize()/2));
					}
				}
			}
		}
	}
	
	
	private void seeding(StringBuilder readSequence)
	{
		this.seeds = new ArrayList<Integer>();
		boolean found=false;
		int counter=0;
		int position=-1;
		for(int i=0; i <readSequence.length()-ks.getKmerSize()+1;++i)
		{
			//build kmer
			String kmer = readSequence.substring(i, i+ks.getKmerSize());
			int kmer_count=ks.checkKmerDistribution(kmer);
			if(kmer_count!=0 && kmer_count<this.kmer_count_limit)
			{
				if(!found)
				{
			//		System.out.println("found first one "+ i);
					position=i;
					found=true;
				}
			//	System.out.println("increase counter ");
				++counter;
			}
			else
			{
				//System.out.println("not valid kmer "+ counter+ " "+position);
				if(counter!=0 && kmer_count==0)
				{
				//	System.out.println("not rep kmer");
					int halfResult=counter/2;
					if(entropy.calcEntropy(readSequence.substring(position + halfResult, position + halfResult+ks.getKmerSize()))>=this.entropy_cutoff)
					seeds.add(position+halfResult);
					position=-1;
					counter=0;
					found=false;
				}
				else if(found)
				{
					++counter;
				}
			}
		}
		
		//System.out.println(seeds.toString());
		if(counter!=0)
		{
			//System.out.println("counter is not 0 "+counter+ " "+position);
			int halfResult=counter/2;
			//System.out.println(halfResult);
			//if(position+halfResult-ks.getKmerSize()<=seeds.get(seeds.size()-1)+ks.getKmerSize())
			{
				//int dis = position+halfResult-seeds.get(seeds.size()-1)/2;
				if(entropy.calcEntropy(readSequence.substring(position + halfResult, position + halfResult+ks.getKmerSize()))>=this.entropy_cutoff)
				seeds.add(position+halfResult);
			}
		}
	}

	public boolean isContamination() {
		return contamination;
	}

	public void setContamination(boolean contamination) {
		this.contamination = contamination;
	}


	
}
